package orm

import (
	"errors"
	"database/sql"
	"bytes"
)

type OrmDb interface {
	Read()
	Insert(...interface{})error
	Select(string, *querySet,...interface{})(error)
	Update()
	Delete(string,...interface{}) error
	CreateTable(model *ModelInfo) error
	Query()
	GetDb()(*sql.DB)
	GetDataType()(*map[string]string)
}

type OrmManager struct {
	ModelCache map[string]*ModelInfo
	DbDriver map[string]DbOpen
	OrmDb OrmDb
	Db *sql.DB
	dataType *map[string]string
	Constrain *map[string]string

}

var Orm = &OrmManager{
	ModelCache: make(map[string]*ModelInfo),
	DbDriver:make(map[string]DbOpen),
}

type DbOpen func(config string) (OrmDb,error)

func (orm *OrmManager)RegisterDb(DbName string,open DbOpen)error{

	if open == nil {
		return errors.New("OpenDb is nil")
	}

	if _,ok := Orm.DbDriver[DbName];ok{
		return errors.New("DbDriver is exist")
	}

	Orm.DbDriver[DbName] = open

	return nil
}

func (orm *OrmManager)StartDb(DbName string,config string)(err error){
	DbOpen,ok := orm.DbDriver[DbName]
	if !ok{
		return errors.New("No This Db:"+DbName)
	}

	orm.OrmDb,err = DbOpen(config)
	if err!=nil{
		return
	}

	orm.Db = orm.OrmDb.GetDb()

	orm.dataType = orm.OrmDb.GetDataType()

	return
}

func (orm *OrmManager)RegisterModel(args ...interface{})error{
	if args==nil || len(args)==0 {
		return errors.New("args is empty")
	}

	for _,model := range args{
		modelInfo ,err  := GetModelInfo(model)
		if err!=nil{
			return err
		}
		if _,ok := orm.ModelCache[modelInfo.TableName];ok{
			return errors.New("have duplicate model name")
		}
		orm.ModelCache[modelInfo.TableName] = modelInfo
	}

	for _,mi := range orm.ModelCache{
		if err := orm.OrmDb.CreateTable(mi);err!=nil{
			return err
		}
	}

	return nil
}

func (orm *OrmManager)Insert(arg...interface{})error{
	return orm.OrmDb.Insert(arg...)
}

func (orm *OrmManager)Query(modelName string) (QuerySet) {
	qs := orm.initQuerySet(modelName,"SELECT")
	return qs
}

func (orm *OrmManager)Delete(modelName string)(QuerySet){
	qs := orm.initQuerySet(modelName,"DELETE")
	return qs
}

func (orm *OrmManager)initQuerySet(modelName string,operation string)QuerySet{
	qs := &querySet{}

	ModelInfo,ok := orm.ModelCache[modelName]
	if !ok{
		qs.err = errors.New("no this model")
	}

	qs.ModelInfo = ModelInfo
	qs.Sql = bytes.Buffer{}
	qs.Limit = DefaultLimit
	qs.Conditions = &Condition{
		Conds:[]condValue{},
	}
	qs.Sql.WriteString(operation)
	return qs
}